home *** CD-ROM | disk | FTP | other *** search
/ Scene 96 / Scene 96 International Edition (Zyklop Software) (Disc 2) (1997).iso / misc / coding / midas060 / src / dma.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-16  |  11.9 KB  |  443 lines

  1. /*      DMA.C
  2.  *
  3.  * DMA handling routines
  4.  *
  5.  * $Id: dma.c,v 1.3 1997/01/16 18:41:59 pekangas Exp $
  6.  *
  7.  * Copyright 1996,1997 Housemarque Inc.
  8.  *
  9.  * This file is part of the MIDAS Sound System, and may only be
  10.  * used, modified and distributed under the terms of the MIDAS
  11.  * Sound System license, LICENSE.TXT. By continuing to use,
  12.  * modify or distribute this file you indicate that you have
  13.  * read the license and understand and accept it fully.
  14. */
  15.  
  16. #include "lang.h"
  17. #include "mtypes.h"
  18. #include "errors.h"
  19. #include "mmem.h"
  20. #include "dma.h"
  21.  
  22. #ifdef __DPMI__
  23. #include "dpmi.h"
  24. #endif
  25.  
  26. RCSID(const char *dma_rcsid = "$Id: dma.c,v 1.3 1997/01/16 18:41:59 pekangas Exp $";)
  27.  
  28.  
  29.  
  30. /****************************************************************************\
  31. *       DMA channel data:
  32. \****************************************************************************/
  33.  
  34. dmaChannel      dmaChannels[8] = {
  35.     { 0, 1, 0x00, 0x01, 0x09, 0x0A, 0x0B, 0x0C, 0x87 },
  36.     { 1, 2, 0x02, 0x03, 0x09, 0x0A, 0x0B, 0x0C, 0x83 },
  37.     { 2, 4, 0x04, 0x05, 0x09, 0x0A, 0x0B, 0x0C, 0x81 },
  38.     { 3, 8, 0x06, 0x07, 0x09, 0x0A, 0x0B, 0x0C, 0x82 },
  39.     { 4, 1, 0xC0, 0xC2, 0xD2, 0xD4, 0xD6, 0xD8, 0x8F },
  40.     { 5, 2, 0xC4, 0xC6, 0xD2, 0xD4, 0xD6, 0xD8, 0x8B },
  41.     { 6, 4, 0xC8, 0xCA, 0xD2, 0xD4, 0xD6, 0xD8, 0x89 },
  42.     { 7, 8, 0xCC, 0xCE, 0xD2, 0xD4, 0xD6, 0xD8, 0x8A } };
  43.  
  44.  
  45.  
  46. /* *!!* */
  47. #ifdef __WATCOMC__
  48.  
  49. #ifdef __16__
  50.  
  51. void outp(unsigned port, unsigned value);
  52. #pragma aux outp = \
  53.     "out    dx,al" \
  54.     parm [dx] [ax] \
  55.     modify exact [];
  56.  
  57. unsigned inp(unsigned port);
  58. #pragma aux inp = \
  59.     "xor    ax,ax" \
  60.     "in     al,dx" \
  61.     parm [dx] \
  62.     value [ax] \
  63.     modify exact [ax];
  64.  
  65. #else
  66.  
  67. void outp(unsigned port, unsigned value);
  68. #pragma aux outp = \
  69.     "out    dx,al" \
  70.     parm [edx] [eax] \
  71.     modify exact [];
  72.  
  73. unsigned inp(unsigned port);
  74. #pragma aux inp = \
  75.     "xor    eax,eax" \
  76.     "in     al,dx" \
  77.     parm [edx] \
  78.     value [eax] \
  79.     modify exact [eax];
  80.  
  81. #endif
  82.  
  83. #endif
  84.  
  85. #ifdef __BORLANDC__
  86.  
  87. void outp(unsigned port, unsigned value)
  88. {
  89. asm     mov     dx,word ptr port
  90. asm     mov     al,byte ptr value
  91. asm     out     dx,al
  92. }
  93.  
  94. unsigned inp(unsigned port)
  95. {
  96. asm     xor     ax,ax
  97. asm     in      al,dx
  98.     return _AX;
  99. }
  100.  
  101. #endif
  102.  
  103.  
  104.  
  105.  
  106. /****************************************************************************\
  107. *
  108. * Function:     int dmaAllocBuffer(unsigned size, dmaBuffer *buf);
  109. *
  110. * Description:  Allocates a DMA buffer (totally inside a 64K physical page)
  111. *
  112. * Input:        unsigned size           size of buffer in bytes
  113. *               dmaBuffer *buf          pointer to DMA buffer information
  114. *
  115. * Returns:      MIDAS error code. DMA buffer information is written to *buf.
  116. *
  117. \****************************************************************************/
  118.  
  119. int CALLING dmaAllocBuffer(unsigned size, dmaBuffer *buf)
  120. {
  121.     int         error;
  122. #ifdef __REALMODE__
  123.     unsigned    bseg;
  124. #endif
  125. #ifdef __DPMI__
  126.     static ulong addr;
  127. #endif
  128.  
  129.     /* Check that buffer size is below 30000 bytes: */
  130.     if ( size > 30000 )
  131.     {
  132.         ERROR(errInvalidArguments, ID_dmaAllocBuffer);
  133.         return errInvalidArguments;
  134.     }
  135.  
  136. #ifdef __REALMODE__
  137.     /* Allocate memory for 2*size bytes, to ensure the block always fits
  138.        in a 64k physical page: */
  139.     if ( (error = memAlloc(2 * size + 16, &buf->memBlk)) != OK )
  140.         PASSERROR (ID_dmaAllocBuffer)
  141.  
  142.     /* bseg = allocated memory block segment: */
  143.     bseg = *((unsigned*) ((uchar*)&buf->memBlk + 2)) +
  144.         (((*(unsigned*) &buf->memBlk) + 15) >> 4);
  145.     /* /me really hates 16-bit compilers */
  146.  
  147.     /* Move buffer to the beginning of the next 64k page if it does not fit
  148.        into current one: */
  149.     if ( (bseg & 0x0FFF) >= (0x0FFF - (size + 15) / 16) )
  150.         bseg = (bseg & 0xF000) + 0x1000;
  151.  
  152.     buf->bufferSeg = bseg;                  /* buffer segment */
  153.     buf->startAddr = ((ulong) bseg) << 4;   /* buffer phys. start address */
  154.  
  155.     /* Create pointer to buffer data: */
  156.     buf->dataPtr = (void*) (((ulong) bseg) << 16);
  157. #else
  158.  
  159.     /* Protected mode under DPMI: */
  160.  
  161.     /* This code assumes in the first megabyte the logical addresses
  162.        correspond to the physical ones (mapped directly). */
  163.  
  164.     /* Allocate DOS memory for DMA buffer: */
  165.     if ( (error = dpmiAllocDOSMem((2 * size + 32 + 1024) / 16, &buf->dosSeg,
  166.         &buf->dpmiSel)) != OK )
  167.         PASSERROR(ID_dmaAllocBuffer);
  168.  
  169.     /* Get the allocated memory block linear start address: */
  170.     if ( (error = dpmiGetSegmentBase(buf->dpmiSel, &addr)) != OK )
  171.         PASSERROR(ID_dmaAllocBuffer);
  172.  
  173.     /* Align to paragraph boundary: */
  174. //    addr = (addr + 15) & 0xFFFFFFF0;
  175.  
  176.     /* Align to 512-byte boundary: */
  177.     addr = (addr + 511) & (~511);
  178.  
  179.     /* Move the buffer in to the beginning of the next 64kb page if it does
  180.        not fit into the current one: */
  181.     if ( (addr & 0xFFFF) >= (0x10000 - size) )
  182.         addr = (addr & 0xFFFF0000) + 0x10000;
  183.  
  184.     buf->startAddr = addr;
  185.  
  186.     /* Lock the DMA buffer memory area: */
  187.     if ( (error = dpmiLockMemory(addr, size)) != OK )
  188.         PASSERROR(ID_dmaAllocBuffer);
  189.  
  190. #ifdef __FLATMODE__
  191.     /* Build pointer to buffer data: */
  192.     buf->dataPtr = (void*) buf->startAddr;
  193.  
  194. #else
  195.     /* Not fully flat memory - allocate descriptor for DMA buffer memory: */
  196.     if ( (error = dpmiAllocDescriptor(&buf->bufferSeg)) != OK )
  197.         PASSERROR(ID_dmaAllocBuffer)
  198.  
  199.     /* Set new segment base and limit to the DMA buffer: */
  200.     if ( (error = dpmiSetSegmentBase(buf->bufferSeg, addr)) != OK )
  201.         PASSERROR(ID_dmaAllocBuffer)
  202.     if ( (error = dpmiSetSegmentLimit(buf->bufferSeg, size)) != OK )
  203.         PASSERROR(ID_dmaAllocBuffer)
  204.  
  205. #ifdef __16__
  206.     /* Build pointer to buffer data: */
  207.     buf->dataPtr = (void*) (((ulong) buf->bufferSeg) << 16);
  208. #else
  209.     /* Build pointer to buffer data: */
  210.     *((ushort*)(((ulong) &buf->dataPtr) + 4) = buf->bufferSeg;  /* segment */
  211.     *((ulong*)(((ulong) &buf->dataPtr)) = 0;  /* offset */
  212. #endif
  213.  
  214. #endif
  215. #endif
  216.  
  217.     buf->bufferLen = size;
  218.     buf->channel = -1;
  219.  
  220.     return OK;
  221. }
  222.  
  223.  
  224.  
  225.  
  226. /****************************************************************************\
  227. *
  228. * Function:     int dmaFreeBuffer(dmaBuffer *buf);
  229. *
  230. * Description:  Deallocates an allocated DMA buffer
  231. *
  232. * Input:        dmaBuffer *buf          pointer to DMA buffer information
  233. *
  234. * Returns:      MIDAS error code
  235. *
  236. \****************************************************************************/
  237.  
  238. int CALLING dmaFreeBuffer(dmaBuffer *buf)
  239. {
  240.     int         error;
  241. #ifdef __REALMODE__
  242.     /* Deallocate buffer memory block: */
  243.     if ( (error = memFree(buf->memBlk)) != OK )
  244.         PASSERROR(ID_dmaFreeBuffer)
  245. #else
  246.     /* Unlock the DMA buffer memory area: */
  247.     if ( (error = dpmiLockMemory(buf->startAddr, buf->bufferLen)) != OK )
  248.         PASSERROR(ID_dmaFreeBuffer);
  249.  
  250.     /* Deallocate DOS memory: */
  251.     if ( (error = dpmiFreeDOSMem(buf->dpmiSel)) != OK )
  252.         PASSERROR(ID_dmaFreeBuffer)
  253.  
  254. #ifndef __FLATMODE__
  255.     /* Deallocate DPMI selector: */
  256.     if ( (error = dpmiFreeSelector(buf->bufferSeg)) != OK )
  257.         PASSERROR(ID_dmaFreeBuffer)
  258. #endif
  259. #endif
  260.  
  261.     return OK;
  262. }
  263.  
  264.  
  265.  
  266.  
  267. /****************************************************************************\
  268. *
  269. * Function:     int dmaPlayBuffer(dmaBuffer *buf, unsigned channel,
  270. *                   unsigned autoInit);
  271. *
  272. * Description:  Plays a DMA buffer
  273. *
  274. * Input:        dmaBuffer *buf          pointer to DMA buffer information
  275. *               unsigned channel        DMA channel number
  276. *               unsigned autoInit       1 if autoinitializing DMA is used, 0
  277. *                                       if not
  278. *
  279. * Returns:      MIDAS error code
  280. *
  281. \****************************************************************************/
  282.  
  283. int CALLING dmaPlayBuffer(dmaBuffer *buf, unsigned channel, unsigned autoInit)
  284. {
  285.     dmaChannel  *chan;
  286.  
  287.     /* Point chan to correct DMA channel information structure: */
  288.     chan = &dmaChannels[channel];
  289.  
  290.     buf->channel = channel;
  291.  
  292.     /* Reset DMA request: */
  293.     outp(chan->request, channel & 3);
  294.  
  295.     /* Mask out the channel: */
  296.     outp(chan->singleMask, (channel & 3) | 4);
  297.  
  298.     if ( autoInit )
  299.     {
  300.         /* Read mode, single mode, autoinitialization: */
  301.         outp(chan->mode, (channel & 3) | 8 | 16 | 64);
  302.     }
  303.     else
  304.     {
  305.         /* Read mode, single mode, no autoinitialization: */
  306.         outp(chan->mode, (channel & 3) | 8 | 64);
  307.     }
  308.  
  309.     /* Set DMA page: */
  310.     outp(chan->page, (buf->startAddr >> 16L));
  311.  
  312.     /* Clear byte pointer flip-flop so that next write to a 16-bit register
  313.        will go to the low byte: */
  314.     outp(chan->clearFF, 0);
  315.  
  316.     /* Set DMA start address and buffer length: */
  317.     if ( channel < 4 )
  318.     {
  319.         /* 8-bit DMA channel - just set the address: */
  320.         outp(chan->baseAddr, (buf->startAddr & 0xFF));
  321.         outp(chan->baseAddr, (buf->startAddr & 0xFFFF) >> 8);
  322.  
  323.         /* Set word count: */
  324.         outp(chan->wordCount, (buf->bufferLen-1) & 0xFF);
  325.         outp(chan->wordCount, ((buf->bufferLen-1) & 0xFFFF) >> 8);
  326.     }
  327.     else
  328.     {
  329.         /* 16-bit DMA channel - divide address by 2 (starting word) */
  330.         outp(chan->baseAddr, (buf->startAddr >> 1) & 0xFF);
  331.         outp(chan->baseAddr, ((buf->startAddr >> 1) & 0xFFFF) >> 8);
  332.  
  333.         /* Set word count: */
  334.         outp(chan->wordCount, ((buf->bufferLen >> 1)-1) & 0xFF);
  335.         outp(chan->wordCount, (((buf->bufferLen >> 1)-1) & 0xFFFF) >> 8);
  336.     }
  337.  
  338.     /* Enable channel: */
  339.     outp(chan->singleMask, channel & 3);
  340.  
  341.     return OK;
  342. }
  343.  
  344.  
  345.  
  346.  
  347. /****************************************************************************\
  348. *
  349. * Function:     int dmaStop(unsigned channel);
  350. *
  351. * Description:  Stops DMA playing
  352. *
  353. * Input:        unsigned channel        DMA channel number
  354. *
  355. * Returns:      MIDAS error code
  356. *
  357. \****************************************************************************/
  358.  
  359. int CALLING dmaStop(unsigned channel)
  360. {
  361.     dmaChannel  *chan;
  362.  
  363.     /* Point chan to correct DMA channel info structure: */
  364.     chan = &dmaChannels[channel];
  365.  
  366.     /* Mask out channel: */
  367.     outp(chan->singleMask, (channel & 3) | 4);
  368.  
  369.     /* Clear byte pointer flip-flop: */
  370.     outp(chan->clearFF, 0);
  371.  
  372.     return OK;
  373. }
  374.  
  375.  
  376.  
  377.  
  378. /****************************************************************************\
  379. *
  380. * Function:     int dmaGetPos(dmaBuffer *buf, unsigned *pos);
  381. *
  382. * Description:  Reads the DMA playing position
  383. *
  384. * Input:        dmaBuffer *buf          pointer to DMA buffer information
  385. *               unsigned *pos           pointer to playing position
  386. *
  387. * Returns:      MIDAS error code. DMA playing position from the beginning
  388. *               of the buffer, in bytes, is written to *pos.
  389. *
  390. \****************************************************************************/
  391.  
  392. int CALLING dmaGetPos(dmaBuffer *buf, unsigned *pos)
  393. {
  394.     dmaChannel  *chan;
  395.     int         count1, count2;
  396.  
  397.     /* Point chan to correct DMA channel info structure: */
  398.     chan = &dmaChannels[buf->channel];
  399.  
  400.     /* Clear byte pointer flip-flop: */
  401.     outp(chan->clearFF, 0);
  402.  
  403.     /* Read DMA word count two times until the difference between the
  404.        counts read is below or equal to 4 and the value is legal:
  405.        (make sure we won't be disturbed by Expanded Memory Managers or
  406.        something) */
  407.     do
  408.     {
  409.         count1 = inp(chan->wordCount);
  410.         count1 = count1 + ((inp(chan->wordCount)) << 8);
  411.  
  412.         /* Convert count1 to number of bytes if the channel is 16-bit: */
  413.         if ( buf->channel > 3 )
  414.             count1 = count1 << 1;
  415.  
  416.         count2 = inp(chan->wordCount);
  417.         count2 = count2 + ((inp(chan->wordCount)) << 8);
  418.  
  419.         /* Convert count2 to number of bytes if the channel is 16-bit: */
  420.         if ( buf->channel > 3 )
  421.             count2 = count2 << 1;
  422.     } while ( ((count1 - count2) > 4) || ((count1 - count2) < (-4)) ||
  423.         (count1 >= buf->bufferLen) );
  424.  
  425.     /* Write position to *pos: */
  426.     *pos = buf->bufferLen - count1;
  427.  
  428.     return OK;
  429. }
  430.  
  431.  
  432. /*
  433.  * $Log: dma.c,v $
  434.  * Revision 1.3  1997/01/16 18:41:59  pekangas
  435.  * Changed copyright messages to Housemarque
  436.  *
  437.  * Revision 1.2  1996/10/13 17:06:54  pekangas
  438.  * Now properly masks out channel in dmaStop()
  439.  *
  440.  * Revision 1.1  1996/05/22 20:49:33  pekangas
  441.  * Initial revision
  442.  *
  443. */